package com.kinyx.framework.file;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import com.kinyx.framework.annotation.assist.CanNotNull;
import com.kinyx.framework.annotation.assist.CanNull;
import com.kinyx.framework.annotation.assist.ReturnNotNull;
import com.kinyx.framework.annotation.assist.Throws;
import com.kinyx.framework.enums.impl.ErrorCode0;
import com.kinyx.framework.enums.impl.ErrorCodeX;
import com.kinyx.framework.exception.KRuntimeException;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * 文件读取工具类
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class KFileReader {

	/**
	 * 打开输入流
	 * @param fileName 文件名（相对路径）
	 * @return 输入流
	 */
	@ReturnNotNull
	@Throws(at = "KFileReader.openStream(String)", value = "ErrorCode0.FileDoesNotExists", when = "ClassLoader.getSystemResource(String) == null")
	@Throws(at = "KFileReader.openStream(String)", value = "IOException", when = "URL.openStream()")
	public static InputStream openStream(@CanNotNull final String fileName) {
		if (fileName.startsWith("/")) { return openStream(fileName.substring(1)); }
		final URL url = ClassLoader.getSystemResource(fileName);
		if (url == null) {
			throw new KRuntimeException(ErrorCode0.FileDoesNotExists).param("fileName", fileName);
		}
		final InputStream is;
		try {
			is = url.openStream();
		} catch (final IOException e) {
			throw new KRuntimeException(e);
		}
		return is;
	}

	/**
	 * 读取文件内容
	 * @param fileName 文件名（相对路径）
	 * @param charset 字符集
	 * @return 字符串结果
	 */
	@ReturnNotNull
	@Throws(at = "KReader.read(InputStream, String)", value = "UnsupportedEncodingException", when = "new InputStreamReader(InputStream, String)")
	@Throws(at = "KReader.read(InputStream, String)", value = "IOException", when = "InputStreamReader.read()")
	@Throws(at = "KFileReader.openStream(String)", value = "ErrorCode0.FileDoesNotExists", when = "ClassLoader.getSystemResource(String) == null")
	@Throws(at = "KFileReader.openStream(String)", value = "IOException", when = "URL.openStream()")
	public static String read(@CanNotNull final String fileName, @CanNull(defaultValue = "Configs.default_KReader_charset") final String charset) {
		return KReader.read(openStream(fileName), charset);
	}

	/**
	 * 读取文件内容
	 * @param fileName 文件名（相对路径）
	 * @return 字节数组结果
	 */
	@ReturnNotNull
	@Throws(at = "KReader.readBytes(InputStream)", value = "IOException", when = "InputStream.available()")
	@Throws(at = "KReader.readBytes(InputStream)", value = "IOException", when = "InputStream.read(byte[])")
	@Throws(at = "KFileReader.openStream(String)", value = "ErrorCode0.FileDoesNotExists", when = "ClassLoader.getSystemResource(String) == null")
	@Throws(at = "KFileReader.openStream(String)", value = "IOException", when = "URL.openStream()")
	public static byte[] readBytes(@CanNotNull final String fileName) {
		return KReader.readBytes(openStream(fileName));
	}

	/**
	 * 新建文件输入流
	 * @param file 文件对象
	 * @return 文件输入流
	 */
	@ReturnNotNull
	@Throws(at = "KFileUtils.checkFile(File)", value = "ErrorCode0.FileDoesNotExists", when = "!File.exists()")
	@Throws(at = "KFileUtils.checkFile(File)", value = "ErrorCode0.IsNotAFile", when = "!File.isFile()")
	@Throws(at = "KFileUtils.checkReadable(File)", value = "ErrorCode0.FileIsNotReadable", when = "!File.canRead()")
	//	@Throws(at = "KFileReader.newFileInputStream(File)", value = "FileNotFoundException", when = "new FileInputStream(File)")
	public static FileInputStream newFileInputStream(@CanNotNull final File file) {
		KFileUtils.checkFile(file);
		KFileUtils.checkReadable(file);
		FileInputStream is;
		try {
			is = new FileInputStream(file);
		} catch (final FileNotFoundException e) {// never happens
			throw new KRuntimeException(e, ErrorCodeX.NeverHappens);
		}
		return is;
	}

	/**
	 * 读取文件内容
	 * @param file 文件对象
	 * @param charset 字符集
	 * @return 字符串结果
	 */
	@ReturnNotNull
	@Throws(at = "KReader.read(InputStream, String)", value = "UnsupportedEncodingException", when = "new InputStreamReader(InputStream, String)")
	@Throws(at = "KReader.read(InputStream, String)", value = "IOException", when = "InputStreamReader.read()")
	@Throws(at = "KFileUtils.checkFile(File)", value = "ErrorCode0.FileDoesNotExists", when = "!File.exists()")
	@Throws(at = "KFileUtils.checkFile(File)", value = "ErrorCode0.IsNotAFile", when = "!File.isFile()")
	@Throws(at = "KFileUtils.checkReadable(File)", value = "ErrorCode0.FileIsNotReadable", when = "!File.canRead()")
	//	@Throws(at = "KFileReader.newFileInputStream(File)", value = "FileNotFoundException", when = "new FileInputStream(File)")
	public static String read(@CanNotNull final File file, @CanNull(defaultValue = "Configs.default_KReader_charset") final String charset) {
		return KReader.read(newFileInputStream(file), charset);
	}

	/**
	 * 读取文件内容
	 * @param file 文件对象
	 * @return 字节数组结果
	 */
	@ReturnNotNull
	@Throws(at = "KReader.readBytes(InputStream)", value = "IOException", when = "InputStream.available()")
	@Throws(at = "KReader.readBytes(InputStream)", value = "IOException", when = "InputStream.read(byte[])")
	@Throws(at = "KFileUtils.checkFile(File)", value = "ErrorCode0.FileDoesNotExists", when = "!File.exists()")
	@Throws(at = "KFileUtils.checkFile(File)", value = "ErrorCode0.IsNotAFile", when = "!File.isFile()")
	@Throws(at = "KFileUtils.checkReadable(File)", value = "ErrorCode0.FileIsNotReadable", when = "!File.canRead()")
	//	@Throws(at = "KFileReader.newFileInputStream(File)", value = "FileNotFoundException", when = "new FileInputStream(File)")
	public static byte[] readBytes(@CanNotNull final File file) {
		return KReader.readBytes(newFileInputStream(file));
	}

}
